分类
联系方式
  1. 新浪微博
  2. E-mail

DartVM PortMap

介绍

PortMap 用于管理所有 Isolate 的端口,它在虚拟机中是一个单例。

PortMap 在虚拟机中有 2 个,Dart 层有一个,C/C++ 层有一个。

PortMap(Dart)

位于 _RawReceivePortImpl 类中,是一个静态成员,签名:

static final _portMap = <int, Map<String, dynamic>>{};

PortMap(C++)

位于 runtime/vm/port.h,该类继承自 AllStatic,是一个纯静态类。

PortSet 数据结构

PortMap 中维护了一个数据结构 PortSet(runtime/vm/port_set.h)具体结构还没有细看,暂且理解为一个哈西表,声明为:

static PortSet<Entry>* ports_;

Entry 是一个 struct,结构如下:

struct Entry : public PortSet<Entry>::Entry {
  Entry() : handler(nullptr), state(kNewPort) {}

  MessageHandler* handler;
  PortState state;
};

整体结构图:

其中:PortState 表示端口的生命周期,共有 4 个:kNewPort、kLivePort、kControlPort、kInactivePort。

CreatePort 创建端口

创建一个端口,拥有虚拟机全局唯一 id:

static Dart_Port CreatePort(MessageHandler* handler);

实现:

Dart_Port PortMap::CreatePort(MessageHandler* handler) {
  //……
  // 创建一个全局唯一的端口号
  const Dart_Port port = AllocatePort();

  // The MessageHandler::ports_ is only accessed by [PortMap], it is guarded
  // by the [PortMap::mutex_] we already hold.
  // 将端口号也告诉 handler
  MessageHandler::PortSetEntry isolate_entry;
  isolate_entry.port = port;
  handler->ports_.Insert(isolate_entry);

  // 这是 portSet 的 Entry
  Entry entry;
  entry.port = port;
  entry.handler = handler;
  entry.state = kNewPort;
  ports_->Insert(entry);

  return entry.port;
}

其中:

  • 全局唯一端口号是在这里分配的
  • 端口号会记录在 MessageHandler 中
  • 将新端口-handler保存到 PortSet 中

最广泛的使用处是 RawReceivePortImpl_factory 方法:

DEFINE_NATIVE_ENTRY(RawReceivePortImpl_factory, 0, 2) {
  ASSERT(
      TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
  GET_NON_NULL_NATIVE_ARGUMENT(String, debug_name, arguments->NativeArgAt(1));
  Dart_Port port_id = PortMap::CreatePort(isolate->message_handler());
  return ReceivePort::New(port_id, debug_name, false /* not control port */);
}

现在知道了:

  • 传入的 isolate->message_handler,新创建的端口会存入 message_handler 的 ports_
    • isolate->message_handler 会同事关联多个 port(ReceivePort)
  • port 的 id 同时也会存入 ReceivePort 的 id 中

PostMessage 发消息

向 Port 发消息的底层方法,就是 PostMessage。

bool PortMap::PostMessage(std::unique_ptr<Message> message,
                          bool before_events) {
  MutexLocker ml(mutex_);
  auto it = ports_->TryLookup(message->dest_port());
  if (it == ports_->end()) {
    // Ownership of external data remains with the poster.
    message->DropFinalizers();
    return false;
  }
  MessageHandler* handler = (*it).handler;
  ASSERT(handler != nullptr);
  handler->PostMessage(std::move(message), before_events);
  return true;
}

其中:

  • 消息中是包含端口号的,首先从 PortMap 中根据端口号拿出 Entry
  • 从 Entry 中就能拿到 handler
  • 然后调用 handler 的 PostMessage
    • 可以看处 PortMap 只是一个转发过程,具体向消息队列加消息的操作,是由 MessageHandler 自己实现的